我在之前写了《一步一步DIY jQuery库》系列文章,然后发现再往下进行研究jQuery库的时候,由于jQuery库做了很多兼容IE6-8的内容,使其看起来比较繁琐,这也造成了jQuery源码的不宜读性。所幸作为移动端的jQuery库替代品-Zepto,是一个轻量级的针对现代高级浏览器的JavaScript库, 它与jquery有着类似的api。Zepto的设计目的是提供 jQuery 的类似的API,但并不是100%覆盖 jQuery。
接下来我们会用一系列博客一边研究Zepto源码,一边DIY一个Zepto库。
基于Zepto 1.2.0版本。
代码挂在我的github上,第一篇博客对应文件夹v0.1。
https://github.com/zrysmt/DIY-zepto
1.下载源码并且编译
在github中:https://github.com/madrobby/zeptoclone下源码,使用下面的命令在命令行编译:1
2npm install
npm run dist
生成源码文件1
2`dist/zepto.js`
`dist/zepto.min.js`
2.整体结构
1 | var Zepto = (function() { |
Zepto没有提供noConflict
命名冲突处理机制,$
被占用后,就只能用Zepto
。
3.无new化处理结构
使用1
console.log($('<p></p>'));//生成dom(<p></p>)
1 | $ = function(selector, context) { |
实际上是调用zepto.init
1
2
3
4zepto.init = function(selector, context) {
//... ...
return zepto.Z(dom, selector);
}
调用zepto.Z
1
2
3
4
5
6
7
8
9
10
11function Z(dom, selector) {
var i, len = dom ? dom.length : 0;
for (i = 0; i < len; i++) {
this[i] = dom[i];
this.length = len;//NodeList对象一定要有length属性
this.selector = selector || '';//选择符
}
}
zepto.Z = function(dom, selector) {
return new Z(dom, selector);//在这里使用new实例化
}
4.传参 形如$('<p></p>')
4.1 定义要使用的变量
1 | var emptyArray = [], |
4.2 要使用的工具函数
- 判断类型模块
这些函数都比较好理解1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25function type(obj) {
return obj == null ? String(obj) :
class2type[toString.call(obj)] || "object";
}
function isWindow(obj) {
return obj != null && obj == obj.window
}
function isObject(obj) {
return type(obj) == "object";
}
isArray = Array.isArray ||
function(object){ return object instanceof Array }
function likeArray(obj) {
var length = !!obj && 'length' in obj && obj.length,
type = $.type(obj)
return 'function' != type && !isWindow(obj) && (
'array' == type || length === 0 ||
(typeof length == 'number' && length > 0 && (length - 1) in obj)
)
}
//加上下面这些就可以使用类型判断了
$.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
- 在
$
后面定义
1 | $.trim = function(str) { |
4.4 zepto.init
1 | zepto.init = function(selector, context) { |
4.5 zepto.fragment
1 | /** |
整体流程:1
$('<p></p>')
zepto.init
–> @1 –>zepto.fragment
–> @2 –> 函数 $ –> zepto.init
–> @3 –> return zepto.Z(dom, selector)
–> 函数 Z 返回结果。
5.$.fn
扩展
1 | $.fn = { |
使用时候:1
2console.log($.fn.log());//测试
console.log($('div').log());//undefined
$('div')
返回的是Z对象(isZ返回true)。
注意,我们在外部只暴露了Zepto
,zepto
,Z
,zepto.Z
是内部的变量。
我们缺少一步,将Z.prototype
指向$.fn
1
zepto.Z.prototype = Z.prototype = $.fn;
这个时候我们的例子都能正确使用了。
6.链式调用
其实原理很简单,只要return this;
即可。
在$.fn
中需要1
2
3
4$.fn = {
constructor: zepto.Z,
length: 0,//为了链式调用能够return this;
};
constructor
和length
都是为了指定this的constructor,增加默认length属性。而且我们在设置zepto.Z.prototype = Z.prototype = $.fn;
等于重写了zepto.Z
和Z
的原型链,需要使用constructor: zepto.Z
重新使原型链连接上。
7.$.extend
扩展
- 工具
1 | function isPlainObject(obj) { |
$.extend
1 | $.extend = function(target) { |
- extend函数
1 | function extend(target, source, deep) { |
例子1
2
3
4
5
6
7
8
9
10
11
12var target = {
one: 'patridge',
three: ["apple", "patato"],
five: { w: "10", a: "20" }
},
source2 = {
three: ["apple1", "patato1", "abc"],
four: "orange",
five: { w: "100", h: "200" }
};
// console.log($.extend(target, source2));//差别:five:{w:"100",h:"200"}
console.log($.extend(true,target, source2));//差别:five:{a:"20" h:"200" w:"100"}
8.CSS选择器查询
完成zepto.init
中TODO:带有上下文和css查询。毕竟这些才是我们经常使用的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31zepto.init = function(selector, context) {
var dom;
//未传参,返回空Zepto对象
if (!selector) {
console.log("未传参数");
return zepto.Z();
} else if (typeof selector == 'string') {
selector = selector.trim();
//如果是“<>”,基本的html代码时
if (selector[0] == '<' && fragmentRE.test(selector)) {
console.log(selector, RegExp.$1);
//调用片段生成dom
dom = zepto.fragment(selector, RegExp.$1, context), selector = null;
//TODO:带有上下文和css查询
/********增加代码*****************************************/
} else if (context !== undefined) {
return $(context).find(selector);
} else {
dom = zepto.qsa(document, selector)
}
/******************************************************/
} //如果selector是一个Zepto对象,返回它自己
else if (zepto.isZ(selector)) {
return selector;
} else {
if (isObject(selector)) {
dom = [selector], selector = null;
}
}
return zepto.Z(dom, selector);
}
qsa函数定义
需要的工具和变量
1 | simpleSelectorRE = /^[\w-]*$/;//全局变量 |
- qsa函数
1 | /** |
这篇博文已经把Zepto的基本架构构建出来了,当然这远远不够,甚至zepto.init
都没完全实现,下一篇博文将首先完全实现zepto.init
代码挂在我的github上,第一篇博客对应文件夹v0.1。
参考阅读: